﻿/* 
    Copyright 2010 MCSharp team Licensed under the
	Educational Community License, Version 2.0 (the "License").
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ForgeCraft
{
    public class MobEntity : Entity
    {
        public Creature creature;

        public int timeInWorld = 0;

        //private List<WPPos> Waypoints = new List<WPPos>();
        //private struct WPPos { public string type; public int seconds, rotspeed; public Position position; }

        //private int currentPoint = 0;

        private System.Timers.Timer mobTimer = new System.Timers.Timer(500);
        private System.Timers.Timer moveTimer = new System.Timers.Timer(200);
        private System.Timers.Timer jumpTimer = new System.Timers.Timer(95);

        private Position newPosition;
        

        //public int metadata;
        //public int health;
        //public int movementSpeed = 72;

        bool movement = false;
        //public bool godMob = false;

        //public byte mobType;

       // bool hunt = true;
        
        bool jumping = false;
        int currentjump = 0;

        //private int countdown = 0;

        public MobEntity(int x, int y, int z, Creature _creature, World world)
        {
            pos.x = (double)x; pos.y = (double)y; pos.z = (double)z;
            inWorld = world;
            creature = _creature;
            creature.health = 16;

            Spawned = true;

            base.Update();
            inWorld.mobsList.Add(this);

            mobTimer.Elapsed += delegate
            {
                int currentNum, foundNum = (32 * 75);
                Random rand = new Random();

                // Zombies damage player when close
                foreach (Player p in inWorld.playerList)
                {

                    Logger.Log("MobSeen: " + Math.Abs(p.pos.x - pos.x) + " " + Math.Abs(p.pos.z - pos.z), LogType.Debug);

                    if (Math.Abs(p.pos.x - pos.x) <= creature.attackRange)
                    {
                        if (Math.Abs(p.pos.y - pos.y) < 1)
                        {
                            if (Math.Abs(p.pos.z - pos.z) <= creature.attackRange)
                            {
                                p.Damage(2, Enum.Parse(typeof(MobType), creature.mobType.ToString()).ToString());
                            }
                        }
                    }
                }

                /*if (Waypoints.Count < 1)
                {
                    if (hunt)*/
                        foreach (Player p in inWorld.playerList)
                        {
                            if (inWorld.IsInPlayersChunk(p, pos))
                            {
                                currentNum = (int)Math.Abs(p.pos.x - pos.x) + (int)Math.Abs(p.pos.y - pos.y) + (int)Math.Abs(p.pos.z - pos.z);
                                if (currentNum < foundNum && currentNum < creature.sightRange)
                                {
                                    //Logger.Log("MobSeen: " + p.name + " " + currentNum, LogType.Debug);
                                    foundNum = currentNum;
                                    newPosition = new Position(p.pos.x, p.pos.y, p.pos.z);
                                    movement = true;
                                }
                            }
                        }
                        /*}
                        else
                        {
                            bool skip = false;
                            movement = false;

                        retry: switch (Waypoints[currentPoint].type)
                            {
                                case "walk":
                                    newPosition = Waypoints[currentPoint].position;
                                    movement = true;

                                    if (pos.x == Waypoints[currentPoint].position.x)
                                    {
                                        if (pos.z == Waypoints[currentPoint].position.z)
                                        {
                                            pos.rotation = Waypoints[currentPoint].position.rotation;
                                            pos.pitch = Waypoints[currentPoint].position.pitch;
                                            currentPoint++;
                                            movement = false;

                                            if (currentPoint == Waypoints.Count) currentPoint = 0;
                                            if (!skip) { skip = true; goto retry; }
                                        }
                                    }
                                    break;
                                case "teleport":
                                    pos = Waypoints[currentPoint].position;
                                    currentPoint++;
                                    if (currentPoint == Waypoints.Count) currentPoint = 0;
                                    return;
                                case "wait":
                                    if (countdown != 0)
                                    {
                                        countdown--;
                                        if (countdown == 0)
                                        {
                                            currentPoint++;
                                            if (currentPoint == Waypoints.Count) currentPoint = 0;
                                            if (!skip) { skip = true; goto retry; }
                                        }
                                    }
                                    else
                                    {
                                        countdown = Waypoints[currentPoint].seconds;
                                    }
                                    return;
                                case "spin":
                                    if (countdown != 0)
                                    {
                                        countdown--;

                                        if (pos.rotation + (byte)Waypoints[currentPoint].rotspeed > 128) pos.rotation = 0;
                                        else if (pos.rotation + (byte)Waypoints[currentPoint].rotspeed < 0) pos.rotation = 128;
                                        else pos.rotation += (byte)Waypoints[currentPoint].rotspeed;

                                        if (countdown == 0)
                                        {
                                            currentPoint++;
                                            if (currentPoint == Waypoints.Count) currentPoint = 0;
                                            if (!skip) { skip = true; goto retry; }
                                        }
                                    }
                                    else
                                    {
                                        countdown = Waypoints[currentPoint].seconds;
                                    }
                                    return;
                                case "speed":
                                    movementSpeed = (int)Math.Round((decimal)((decimal)24 / (decimal)100 * (decimal)Waypoints[currentPoint].seconds));
                                    if (movementSpeed == 0) movementSpeed = 1;

                                    currentPoint++;
                                    if (currentPoint == Waypoints.Count) currentPoint = 0;
                                    if (!skip) { skip = true; goto retry; }
                                    return;
                                case "reset":
                                    currentPoint = 0;
                                    return;
                                case "jump":
                                    jumpTimer.Elapsed += delegate
                                    {
                                        currentjump++;
                                        switch (currentjump)
                                        {
                                            case 1:
                                            case 2: pos.y += 1; break;
                                            case 3: break;
                                            case 4: pos.y += 1; break;
                                            case 5: pos.y += 1; jumping = false; currentjump = 0; jumpTimer.Stop(); break;
                                        }
                                    };
                                    jumpTimer.Start();

                                    currentPoint++;
                                    if (currentPoint == Waypoints.Count) currentPoint = 0;
                                    if (!skip) { skip = true; goto retry; }
                                    break;
                            }

                            if (currentPoint == Waypoints.Count) currentPoint = 0;
                        }*/

                        if (!movement)
                        {
                            if (pos.rotation < 128) pos.rotation += 8;
                            else pos.rotation = 0;

                            if (pos.pitch > 32 && pos.pitch < 64) pos.pitch = 224;
                            else if (pos.pitch > 250) pos.pitch = 0;
                            else pos.pitch += 1;
                        }
            };

            mobTimer.Start();

            moveTimer.Elapsed += delegate
            {
                moveTimer.Interval = creature.movementSpeed; // 200 is the world update timer.
                if (!movement) return;

                //pos.rotation = newPosition.rotation + 128;
                //pos.pitch = newPosition.pitch;

                Logger.Log("MovDifference: " + Math.Sign(newPosition.x - pos.x) + " " + Math.Sign(newPosition.y - pos.y) + " " + Math.Sign(newPosition.z - pos.z), LogType.Debug);

                /*pos.x += Math.Sign(newPosition.x - pos.x) * 0.25;
                //pos.y += 0;
                pos.z += Math.Sign(newPosition.z - pos.z) * 0.25;*/

                Random rand = new Random();

                /*if ((pos.y - 19) % 32 != 0 && !jumping)
                {
                    pos.y = (ushort)((pos.y +) - (pos.y % 32));
                }*/

                x = (int)(pos.x + (Math.Sign(newPosition.x - pos.x)));
                y = (int)pos.y - 1;
                z = (int)(pos.z + (Math.Sign(newPosition.z - pos.z)));

                Block b = (Block)inWorld.GetBlock(x, (byte)y, z).type;
                Block b1, b2, b3;//, b4;

                if (Blocks.PassThroughBlocks.Contains(b) && !jumping)
                {
                    pos.y = pos.y - 0.5;
                }

                //y = (int)(pos.y - 1);   //Block below feet

                //newNum = ((x + Math.Sign(newPosition.x - pos.x), y, (z + Math.Sign(newPosition.z - pos.z));

                b = (Block)inWorld.GetBlock(x, (byte)y, z).type;
                b1 = (Block)inWorld.GetBlock(x, (byte)(y + 1), z).type;
                b2 = (Block)inWorld.GetBlock(x, (byte)(y + 2), z).type;
                b3 = (Block)inWorld.GetBlock(x, (byte)(y + 3), z).type;

                if (Blocks.PassThroughBlocks.Contains(b2) && Blocks.PassThroughBlocks.Contains(b3) && !Blocks.PassThroughBlocks.Contains(b1))
                {   //Get ready to go up step
                    pos.x += Math.Sign(newPosition.x - pos.x) * 0.2;
                    pos.y += 0.5;
                    pos.z += Math.Sign(newPosition.z - pos.z) * 0.2;
                }
                else if (Blocks.PassThroughBlocks.Contains(b1) && Blocks.PassThroughBlocks.Contains(b2))
                {   //Stay on current level
                    pos.x += Math.Sign(newPosition.x - pos.x) * 0.2;
                    pos.z += Math.Sign(newPosition.z - pos.z) * 0.2;
                }
                else if (Blocks.PassThroughBlocks.Contains(b) && Blocks.PassThroughBlocks.Contains(b1))
                {   //Drop a level
                    pos.x += Math.Sign(newPosition.x - pos.x) * 0.2;
                    pos.y -= 0.5;
                    pos.z += Math.Sign(newPosition.z - pos.z) * 0.2;
                }

                //movement = false;
            };
            moveTimer.Start();
        }

        public void RespawnMob()
        {
            inWorld.SendToPlayersInRange(PacketWriter.MakeMobSpawn(this), new Position(this.pos.x, this.pos.y, this.pos.z));
        }

        public void HurtMob(Player hitByPlayer, InventoryItem hitWithItem)
        {
            //TODO: Get item damage value

            inWorld.SendToPlayersInRange(PacketWriter.MakeAnimation(this, 2), new Position(this.pos.x, this.pos.y, this.pos.z));
            inWorld.SendToPlayersInRange(PacketWriter.MakeEntityStatus(this, 2), new Position(this.pos.x, this.pos.y, this.pos.z));

            creature.health -= 5;

            if (creature.health > 0) return;

            Server.SendToAll(PacketWriter.MakeEntityStatus(this, 3));

            if (creature.mobType == (byte)MobType.Sheep)
            {
                // TODO: Spawn blocks of wool
                //if (creature.metadata != 0x10)
                //{
                    //new PickupEntity(x, y, z, new InventoryItem((short)cb.type, 1, cb.metadata), this);
                //}
            }

            Spawned = false;
        }

        public void HurtMob(byte damage) // Direct damage
        {
            inWorld.SendToPlayersInRange(PacketWriter.MakeAnimation(this, 2), new Position(this.pos.x, this.pos.y, this.pos.z));
            inWorld.SendToPlayersInRange(PacketWriter.MakeEntityStatus(this, 2), new Position(this.pos.x, this.pos.y, this.pos.z));

            creature.health -= damage;

            if (creature.health > 0) return;
             
            Server.SendToAll(PacketWriter.MakeEntityStatus(this, 3));

            Spawned = false;
        }

        override public void Update()
        {
            if (!Spawned) return;

            double dx = pos.x - lastpos.x, dy = pos.y - lastpos.y, dz = pos.z - lastpos.z;
            bool rotchanged = (pos.rotation != lastpos.rotation || pos.pitch != lastpos.pitch);
            lastpos.x = pos.x; lastpos.y = pos.y; lastpos.z = pos.z;
            lastpos.rotation = pos.rotation; lastpos.pitch = pos.pitch;
            if (dx != 0 || dy != 0 || dz != 0 || rotchanged)
            {
                foreach (Player p in inWorld.playerList)
                {
                    if (inWorld.IsInPlayersChunk(p, pos))
                    {
                        p.UpdateEntity(this, dx, dy, dz, rotchanged, false, false);
                    }
                }
            }
        }

        public void IdleMob()
        {
            pos.rotation--;
        }

        override public void Despawn()
        {
            if (Spawned) Spawned = !Spawned;

            mobTimer.Stop();
            mobTimer.Dispose();
            moveTimer.Stop();
            moveTimer.Dispose();

            base.Despawn();
        }

        override public string ToString()
        {
            return "[Entity.Mob " + id + ": " + creature + "]";
        }
    }
}
